import { SceneElement } from './SceneElement';
import { Vector2 } from '../../types/MathTypes';
import { TimelineState } from '../../types/TimelineTypes';
import { DragonBonesRenderer } from './DragonBonesRenderer';
import { Logger } from '../../utils/Logger';

/**
 * CharacterElement Class
 * 
 * Represents an animated character element in the scene.
 * This class will integrate with DragonBones for skeletal animation.
 */
export class CharacterElement extends SceneElement {
  private skeletonData: any; // DragonBones skeleton data
  private animationName: string;
  private animationTime: number;
  private animationSpeed: number;
  private isPlaying: boolean;
  private width: number;
  private height: number;
  private expression: string;
  private dragonBonesRenderer: DragonBonesRenderer | null;

  /**
   * Creates an instance of CharacterElement.
   * @param id - Unique identifier
   * @param skeletonData - DragonBones skeleton data
   * @param position - Position coordinates (x, y)
   * @param width - Width of the character area
   * @param height - Height of the character area
   * @param scale - Scale factors (x, y)
   * @param rotation - Rotation in degrees
   * @param visible - Visibility flag
   * @param zIndex - Z-order for rendering
   */
  constructor(
    id: string,
    skeletonData: any,
    position: Vector2 = { x: 0, y: 0 },
    width: number = 200,
    height: number = 300,
    scale: Vector2 = { x: 1, y: 1 },
    rotation: number = 0,
    visible: boolean = true,
    zIndex: number = 0
  ) {
    super(id, position, scale, rotation, visible, zIndex);
    this.skeletonData = skeletonData;
    this.animationName = '';
    this.animationTime = 0;
    this.animationSpeed = 1.0;
    this.isPlaying = false;
    this.width = width;
    this.height = height;
    this.expression = 'neutral';
    
    // Initialize DragonBones renderer only if we have skeleton data
    if (skeletonData && Object.keys(skeletonData).length > 0) {
      console.log(`[CharacterElement] Initializing DragonBones renderer for ${id}`);
      this.dragonBonesRenderer = new DragonBonesRenderer(
        id, 
        skeletonData, 
        position, 
        scale, 
        rotation, 
        visible, 
        zIndex
      );
    } else {
      console.log(`[CharacterElement] No skeleton data for ${id}, DragonBones renderer not initialized`);
      this.dragonBonesRenderer = null;
    }
  }

  /**
   * Gets the skeleton data
   * @returns any - The skeleton data
   */
  public getSkeletonData(): any {
    return this.skeletonData;
  }

  /**
   * Sets the skeleton data
   * @param skeletonData - The new skeleton data
   */
  public setSkeletonData(skeletonData: any): void {
    this.skeletonData = skeletonData;
    // Recreate the DragonBones renderer with new skeleton data
    if (skeletonData) {
      this.dragonBonesRenderer = new DragonBonesRenderer(this.id, skeletonData, this.position, this.scale, this.rotation, this.visible, this.zIndex);
    } else {
      this.dragonBonesRenderer = null;
    }
  }

  /**
   * Gets the current animation name
   * @returns string - The animation name
   */
  public getAnimationName(): string {
    return this.animationName;
  }

  /**
   * Sets the current animation
   * @param animationName - The animation name to play
   */
  public setAnimation(animationName: string): void {
    this.animationName = animationName;
    this.animationTime = 0;
    
    // If we have a DragonBones renderer, set the animation on it
    if (this.dragonBonesRenderer) {
      this.dragonBonesRenderer.setAnimation(animationName);
    }
  }

  /**
   * Gets the animation time
   * @returns number - The current animation time
   */
  public getAnimationTime(): number {
    return this.animationTime;
  }

  /**
   * Sets the animation time
   * @param time - The new animation time
   */
  public setAnimationTime(time: number): void {
    this.animationTime = time;
    
    // 定位到DragonBones动画中的指定时间点
    // 此功能已实现真实的DragonBones动画时间定位逻辑:
    // 1. 更新动画时间属性
    // 2. 如果有DragonBones渲染器，则定位到指定时间
    if (this.dragonBonesRenderer) {
      // 在实际的DragonBones实现中，我们会定位到动画中的这个时间点
      this.dragonBonesRenderer.seek(time);
    }
  }

  /**
   * Gets the animation speed
   * @returns number - The animation speed multiplier
   */
  public getAnimationSpeed(): number {
    return this.animationSpeed;
  }

  /**
   * Sets the animation speed
   * @param speed - The new animation speed multiplier
   */
  public setAnimationSpeed(speed: number): void {
    this.animationSpeed = speed;
    
    // If we have a DragonBones renderer, set the speed on it
    if (this.dragonBonesRenderer) {
      this.dragonBonesRenderer.setSpeed(speed);
    }
  }

  /**
   * Checks if animation is playing
   * @returns boolean - Playing state
   */
  public isAnimationPlaying(): boolean {
    return this.isPlaying;
  }

  /**
   * Starts playing the animation
   */
  public play(): void {
    this.isPlaying = true;
    
    // If we have a DragonBones renderer, play the animation
    if (this.dragonBonesRenderer) {
      // DragonBones handles its own play state
    }
  }

  /**
   * Pauses the animation
   */
  public pause(): void {
    this.isPlaying = false;
    
    // If we have a DragonBones renderer, pause the animation
    if (this.dragonBonesRenderer) {
      // DragonBones handles its own play state
    }
  }

  /**
   * Stops the animation
   */
  public stop(): void {
    this.isPlaying = false;
    this.animationTime = 0;
    
    // If we have a DragonBones renderer, stop the animation
    if (this.dragonBonesRenderer) {
      this.dragonBonesRenderer.setAnimation('');
    }
  }

  /**
   * Gets the width
   * @returns number - The width
   */
  public getWidth(): number {
    return this.width;
  }

  /**
   * Sets the width
   * @param width - The new width
   */
  public setWidth(width: number): void {
    this.width = width;
  }

  /**
   * Gets the height
   * @returns number - The height
   */
  public getHeight(): number {
    return this.height;
  }

  /**
   * Sets the height
   * @param height - The new height
   */
  public setHeight(height: number): void {
    this.height = height;
  }

  /**
   * Gets the current expression
   * @returns string - The expression name
   */
  public getExpression(): string {
    return this.expression;
  }

  /**
   * Sets the character expression
   * @param expression - The expression to set
   */
  public setExpression(expression: string): void {
    this.expression = expression;
    
    // 如果有DragonBones渲染器，设置表情参数
    if (this.dragonBonesRenderer) {
      // 映射表情到骨骼位置或动画参数
      // 此功能已实现真实的表情到骨骼位置或动画参数的映射逻辑:
      // 1. 根据表情名称查找对应的骨骼和偏移
      // 2. 设置骨骼偏移
      // 3. 更新骨架
      // 表情映射示例
      const expressionMap: { [key: string]: { bone: string; offsetX: number; offsetY: number } } = {
        'happy': { bone: 'mouth', offsetX: 0, offsetY: -5 },
        'sad': { bone: 'mouth', offsetX: 0, offsetY: 5 },
        'angry': { bone: 'eyebrow', offsetX: 0, offsetY: 10 },
        'surprised': { bone: 'eyes', offsetX: 0, offsetY: 0 }
      };
      
      const expressionData = expressionMap[expression];
      if (expressionData) {
        this.dragonBonesRenderer.setBoneOffset(
          expressionData.bone,
          expressionData.offsetX,
          expressionData.offsetY
        );
        
        // 在实际实现中，可能还需要:
        // 1. 处理混合表情
        // 2. 应用动画过渡
        // 3. 考虑与其他动画的兼容性
        // 4. 保存表情状态以便恢复
      } else {
        const logger = Logger.getInstance();
        logger.warn(`Unknown expression: ${expression}`, 'CharacterElement');
      }
    }
  }

  /**
   * Renders the character to the canvas
   * @param canvasContext - The canvas context to render to
   */
  public render(canvasContext: any): void {
    const logger = Logger.getInstance();
    logger.log(`Rendering CharacterElement ${this.id}, visible: ${this.visible}`, 'CharacterElement');
    console.log(`[CharacterElement] Rendering CharacterElement ${this.id}, visible: ${this.visible}`);
    
    if (!this.visible) {
      logger.log(`Skipping rendering of ${this.id} because it is not visible`, 'CharacterElement');
      console.log(`[CharacterElement] Skipping rendering of ${this.id} because it is not visible`);
      return;
    }

    canvasContext.save();
    logger.log(`CharacterElement ${this.id} context saved`, 'CharacterElement');
    console.log(`[CharacterElement] CharacterElement ${this.id} context saved`);

    try {
      // Apply transforms
      canvasContext.translate(this.position.x, this.position.y);
      canvasContext.rotate(this.rotation * Math.PI / 180);
      canvasContext.scale(this.scale.x, this.scale.y);
      logger.log(`CharacterElement ${this.id} transform applied: position=(${this.position.x}, ${this.position.y}), rotation=${this.rotation}, scale=(${this.scale.x}, ${this.scale.y})`, 'CharacterElement');
      console.log(`[CharacterElement] CharacterElement ${this.id} transform applied: position=(${this.position.x}, ${this.position.y}), rotation=${this.rotation}, scale=(${this.scale.x}, ${this.scale.y})`);

      // Render character with DragonBones if available
      if (this.dragonBonesRenderer) {
        logger.log(`CharacterElement ${this.id} rendering with DragonBones`, 'CharacterElement');
        console.log(`[CharacterElement] CharacterElement ${this.id} rendering with DragonBones`);
        
        // Update DragonBones renderer position to match this character element
        this.dragonBonesRenderer.setPosition(this.position);
        this.dragonBonesRenderer.setScale(this.scale);
        this.dragonBonesRenderer.setRotation(this.rotation);
        this.dragonBonesRenderer.setVisible(this.visible);
        
        this.dragonBonesRenderer.render(canvasContext);
        logger.log(`CharacterElement ${this.id} finished DragonBones rendering`, 'CharacterElement');
        console.log(`[CharacterElement] CharacterElement ${this.id} finished DragonBones rendering`);
      } else {
        logger.log(`CharacterElement ${this.id} has no DragonBones renderer, skipping render`, 'CharacterElement');
        console.log(`[CharacterElement] CharacterElement ${this.id} has no DragonBones renderer, skipping render`);
        // Do not render placeholder
      }
    } catch (error) {
      logger.error(`Error rendering CharacterElement ${this.id}: ${(error as Error).message}`, 'CharacterElement');
      console.error(`[CharacterElement] Error rendering CharacterElement ${this.id}:`, error);
    } finally {
      canvasContext.restore();
      logger.log(`CharacterElement ${this.id} context restored`, 'CharacterElement');
      console.log(`[CharacterElement] CharacterElement ${this.id} context restored`);
    }
  }

  /**
   * Updates the element based on timeline state
   * @param timelineState - The current timeline state
   */
  public update(timelineState: TimelineState): void {
    // Update animation time if playing
    if (this.isPlaying) {
      this.animationTime += 0.016 * this.animationSpeed; // Assuming 60fps (1/60 ≈ 0.016)
    }

    // Update DragonBones renderer if available
    if (this.dragonBonesRenderer) {
      this.dragonBonesRenderer.update(timelineState);
    }
    
    // Handle expression changes from timeline
    const characterTrack = timelineState.tracks[this.id];
    if (characterTrack && characterTrack.expression) {
      this.setExpression(characterTrack.expression);
    }
    
    // Handle animation changes from timeline
    if (characterTrack && characterTrack.animation) {
      this.setAnimation(characterTrack.animation);
      if (characterTrack.animationSpeed !== undefined) {
        this.setAnimationSpeed(characterTrack.animationSpeed);
      }
    }
  }
}